declare function print(arg:any) : string;
export class BenchmarkRunner {
	// N is the total number of iteration times
	// default N value is 120
	private N: number = 8;

	// M is the number of scores that are ranked last
	// default M value is 4
	private M: number = 4;

	// allRecords is an Array records all running results
	// except the first result
	private allRecords: number[];

	// worstRecords is an Array records M number of worst results
	private worstRecords: number[];

	// initScore is the first score
	private initScore: number = 0;

	// IterationScore is the average value of  (N results - firstIteration)
	private iterationScore: number = 0;

	// WorstCasesScore is the average value of M worstRecords excluding firstIteration
	private worstCasesScore: number = 0;

	target: Function;

    testName: string;
	constructor(testName: string, target: Function) {
        this.testName = testName;
		this.target = target;
		this.allRecords = new Array(this.N);
		this.worstRecords = new Array(this.M);
	}

	SampleAllData() {
		let sum = 0;
		for (let i = 0; i < this.N; i++) {
			let data = this.target();
			if (i === 0) {
				this.initScore = data;
				continue;
			}
			this.allRecords[i] = data;
			sum += data;
		}
		this.iterationScore = sum / (this.N - 1);
		this.allRecords.sort(function (a, b) {
			return b - a;
		});

		sum = 0;
		for (let j = 0; j < this.M; j++) {
			this.worstRecords[j] = this.allRecords[j];
			sum += this.allRecords[j];
		}
		this.worstCasesScore = sum / this.M;
	}
	
	// Strategy:
	// Geometric Average of three scores
	ComputeScore(): number {
		this.SampleAllData();
		let productsOfScores = this.initScore * this.iterationScore * this.worstCasesScore;
		return Math.pow(productsOfScores, 1/3);
	}

	GetInitScore(): number {
		return this.initScore;
	}

	GetIterationScore(): number {
		return this.iterationScore;
	}

	GetWorstCasesScore(): number {
		return this.worstCasesScore;
	}

    run(){
        print(this.testName + " \t: " + this.ComputeScore() + " \tms");
    }
}